
// let convert;
// try { convert = require('encoding').convert; } catch (e) { }

const INTERNALS = Symbol('Body internals');

// fix an issue where "PassThrough" isn't a named export for node <10
// const PassThrough = Stream.PassThrough;

/**
 * Body mixin
 *
 * Ref: https://fetch.spec.whatwg.org/#body
 *
 * @param   Stream  body  Readable stream
 * @param   Object  opts  Response options
 * @return  Void
 */
export default function Body(body, {
    size = 0,
    timeout = 0
} = {}) {
    // if(body){
    //     if(typeof body == 'string'){

    //     }
    // }
    // if (body == null) {
    //   // body is undefined or null
    //   body = null;
    // } else if (isURLSearchParams(body)) {
    //   // body is a URLSearchParams
    //   body = Buffer.from(body.toString());
    // } else if (isBlob(body)) {
    //   // body is blob
    // // } else if (Buffer.isBuffer(body)) {
    //   // body is Buffer
    // } else if (Object.prototype.toString.call(body) === '[object ArrayBuffer]') {
    //   // body is ArrayBuffer
    //   body = Buffer.from(body);
    // } else if (ArrayBuffer.isView(body)) {
    //   // body is ArrayBufferView
    //   body = Buffer.from(body.buffer, body.byteOffset, body.byteLength);
    // // } else if (body instanceof Stream) {
    //   // body is stream
    // } else {
    //   // none of the above
    //   // coerce to string then buffer
    //   body = Buffer.from(String(body));
    // }
    this[INTERNALS] = {
        body,
        disturbed: false,
        error: null
    };
    this.size = size;
    this.timeout = timeout;

    // if (body instanceof Stream) {
    //   body.on('error', err => {
    //     const error = err.name === 'AbortError'
    //       ? err
    //       : new FetchError(`Invalid response body while trying to fetch ${this.url}: ${err.message}`, 'system', err);
    //     this[INTERNALS].error = error;
    //   });
    // }
}

Body.prototype = {
    get body() {
        return this[INTERNALS].body;
    },

    get bodyUsed() {
        return this[INTERNALS].disturbed;
    },


    /**
     * Decode response as text
     *
     * @return  Promise
     */
    text() {
        if(!this.body){
            //如果没有值
            return this.body;
        }
        if(this.body instanceof ArrayBuffer){

       const   arrayBufferToString = function (buffer) {
       
        var byteArray = new Uint8Array(buffer);
        var byteString = '';
        for (var i = 0; i < byteArray.byteLength; i++) {
          byteString += String.fromCodePoint(byteArray[i]);
        }
        return byteString;
      }
      
      var encodedString =  arrayBufferToString(this.body)
           //对字符串进行解码 没有这一步中文会乱码
        let decodedString = decodeURIComponent(escape(encodedString));
          return decodedString

        }else if(typeof this.body == 'string'){
            return this.body;
        }
        return this.body + "";;
    },

};

// In browsers, all properties are enumerable.
Object.defineProperties(Body.prototype, {
    body: { enumerable: true },
    text: { enumerable: true },
});

Body.mixIn = function (proto) {
    for (const name of Object.getOwnPropertyNames(Body.prototype)) {
        // istanbul ignore else: future proof
        if (!(name in proto)) {
            const desc = Object.getOwnPropertyDescriptor(Body.prototype, name);
            Object.defineProperty(proto, name, desc);
        }
    }
};


/**
 * Detect a URLSearchParams object
 * ref: https://github.com/bitinn/node-fetch/issues/296#issuecomment-307598143
 *
 * @param   Object  obj     Object to detect by type or brand
 * @return  String
 */
function isURLSearchParams(obj) {
    // Duck-typing as a necessary condition.
    if (typeof obj !== 'object' ||
      typeof obj.append !== 'function' ||
      typeof obj.delete !== 'function' ||
      typeof obj.get !== 'function' ||
      typeof obj.getAll !== 'function' ||
      typeof obj.has !== 'function' ||
      typeof obj.set !== 'function') {
        return false;
    }

    // Brand-checking and more duck-typing as optional condition.
    return obj.constructor.name === 'URLSearchParams' ||
      Object.prototype.toString.call(obj) === '[object URLSearchParams]' ||
      typeof obj.sort === 'function';
}

/**
 * Performs the operation "extract a `Content-Type` value from |object|" as
 * specified in the specification:
 * https://fetch.spec.whatwg.org/#concept-bodyinit-extract
 *
 * This function assumes that instance.body is present.
 *
 * @param   Mixed  instance  Any options.body input
 */
export function extractContentType(body) {
    if (body === null) {
        // body is null
        return null;
    } else if (typeof body === 'string') {
        // body is string
        return 'text/plain;charset=UTF-8';
    } else if (isURLSearchParams(body)) {
        // body is a URLSearchParams
        return 'application/x-www-form-urlencoded;charset=UTF-8';
    } else if (isBlob(body)) {
        // body is blob
        return body.type || null;
    } else if (Buffer.isBuffer(body)) {
        // body is buffer
        return null;
    } else if (Object.prototype.toString.call(body) === '[object ArrayBuffer]') {
        // body is ArrayBuffer
        return null;
    } else if (ArrayBuffer.isView(body)) {
        // body is ArrayBufferView
        return null;
    } else if (typeof body.getBoundary === 'function') {
        // detect form data input from form-data module
        return `multipart/form-data;boundary=${body.getBoundary()}`;
    } else if (body instanceof Stream) {
        // body is stream
        // can't really do much about this
        return null;
    } else {
        // Body constructor defaults other things to string
        return 'text/plain;charset=UTF-8';
    }
}

/**
 * The Fetch Standard treats this as if "total bytes" is a property on the body.
 * For us, we have to explicitly get it with a function.
 *
 * ref: https://fetch.spec.whatwg.org/#concept-body-total-bytes
 *
 * @param   Body    instance   Instance of Body
 * @return  Number?            Number of bytes, or null if not possible
 */
export function getTotalBytes(instance) {
    const { body } = instance;

    if (body === null) {
        // body is null
        return 0;
    } else if (isBlob(body)) {
        return body.size;
    } else if (Buffer.isBuffer(body)) {
        // body is buffer
        return body.length;
    } else if (body && typeof body.getLengthSync === 'function') {
        // detect form data input from form-data module
        if (body._lengthRetrievers && body._lengthRetrievers.length == 0 || // 1.x
          body.hasKnownLength && body.hasKnownLength()) { // 2.x
            return body.getLengthSync();
        }
        return null;
    } else {
        // body is stream
        return null;
    }
}

/**
 * Write a Body to a Node.js WritableStream (e.g. http.Request) object.
 *
 * @param   Body    instance   Instance of Body
 * @return  Void
 */
export function writeToStream(dest, instance) {
    const { body } = instance;

    if (body === null) {
        // body is null
        dest.end();
    } else if (isBlob(body)) {
        body.stream().pipe(dest);
    } else if (Buffer.isBuffer(body)) {
        // body is buffer
        dest.write(body);
        dest.end()
    } else {
        // body is stream
        body.pipe(dest);
    }
}

// expose Promise
Body.Promise = Promise;


/**
 * Clone body given Res/Req instance
 *
 * @param   Mixed   instance       Response or Request instance
 * @param   String  highWaterMark  highWaterMark for both PassThrough body streams
 * @return  Mixed
 */
export function clone(instance, highWaterMark) {
    let p1;
    let p2;
    let {body} = instance;

    // Don't allow cloning a used body
    if (instance.bodyUsed) {
        throw new Error('cannot clone body after it is used');
    }

    return body;
}
